<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
    />
    <meta name="description" content="Particle systems for rocket and comet tails." />
    <meta name="cesium-sandcastle-labels" content="Beginner, Showcases, Tutorials" />
    <title>Cesium Demo</title>
    <script type="text/javascript" src="../Sandcastle-header.js"></script>
    <script
      type="text/javascript"
      src="../../../Build/CesiumUnminified/Cesium.js"
      nomodule
    ></script>
    <script type="module" src="../load-cesium-es6.js"></script>
  </head>
  <body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
    <style>
      @import url(../templates/bucket.css);
    </style>
    <div id="cesiumContainer" class="fullSize"></div>
    <div id="loadingOverlay"><h1>Loading...</h1></div>
    <div id="toolbar"></div>
    <script id="cesium_sandcastle_script">
      window.startup = async function (Cesium) {
        "use strict";
        //Sandcastle_Begin
        const viewer = new Cesium.Viewer("cesiumContainer", {
          shouldAnimate: true,
        });
        const planePosition = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883, 800.0);
        const particlesOffset = new Cesium.Cartesian3(
          -8.950115473940969,
          34.852766731753945,
          -30.235411095432937,
        );
        const cameraLocation = Cesium.Cartesian3.add(
          planePosition,
          particlesOffset,
          new Cesium.Cartesian3(),
        );
        const resetCamera = function () {
          viewer.camera.lookAt(cameraLocation, new Cesium.Cartesian3(-450, -300, 200));
        };
        resetCamera();

        // Draw particle image to a canvas
        let particleCanvas;
        function getImage() {
          if (!Cesium.defined(particleCanvas)) {
            particleCanvas = document.createElement("canvas");
            particleCanvas.width = 20;
            particleCanvas.height = 20;
            const context2D = particleCanvas.getContext("2d");
            context2D.beginPath();
            context2D.arc(8, 8, 8, 0, Cesium.Math.TWO_PI, true);
            context2D.closePath();
            context2D.fillStyle = "rgb(255, 255, 255)";
            context2D.fill();
          }
          return particleCanvas;
        }

        // Add plane to scene
        const hpr = new Cesium.HeadingPitchRoll(0.0, Cesium.Math.PI_OVER_TWO, 0.0);
        const orientation = Cesium.Transforms.headingPitchRollQuaternion(
          planePosition,
          hpr,
        );
        const entity = viewer.entities.add({
          model: {
            uri: "../../SampleData/models/CesiumAir/Cesium_Air.glb",
            scale: 3.5,
          },
          position: planePosition,
          orientation: orientation,
        });

        // creating particles model matrix
        const translationOffset = Cesium.Matrix4.fromTranslation(
          particlesOffset,
          new Cesium.Matrix4(),
        );
        const translationOfPlane = Cesium.Matrix4.fromTranslation(
          planePosition,
          new Cesium.Matrix4(),
        );
        const particlesModelMatrix = Cesium.Matrix4.multiplyTransformation(
          translationOfPlane,
          translationOffset,
          new Cesium.Matrix4(),
        );

        // creating the particle systems
        const rocketOptions = {
          numberOfSystems: 50.0,
          iterationOffset: 0.1,
          cartographicStep: 0.000001,
          baseRadius: 0.0005,

          colorOptions: [
            {
              minimumRed: 1.0,
              green: 0.5,
              minimumBlue: 0.05,
              alpha: 1.0,
            },
            {
              red: 0.9,
              minimumGreen: 0.6,
              minimumBlue: 0.01,
              alpha: 1.0,
            },
            {
              red: 0.8,
              green: 0.05,
              minimumBlue: 0.09,
              alpha: 1.0,
            },
            {
              minimumRed: 1,
              minimumGreen: 0.05,
              blue: 0.09,
              alpha: 1.0,
            },
          ],
        };

        const cometOptions = {
          numberOfSystems: 100.0,
          iterationOffset: 0.003,
          cartographicStep: 0.0000001,
          baseRadius: 0.0005,

          colorOptions: [
            {
              red: 0.6,
              green: 0.6,
              blue: 0.6,
              alpha: 1.0,
            },
            {
              red: 0.6,
              green: 0.6,
              blue: 0.9,
              alpha: 0.9,
            },
            {
              red: 0.5,
              green: 0.5,
              blue: 0.7,
              alpha: 0.5,
            },
          ],
        };

        let scratchCartesian3 = new Cesium.Cartesian3();
        let scratchCartographic = new Cesium.Cartographic();
        const forceFunction = function (options, iteration) {
          return function (particle, dt) {
            dt = Cesium.Math.clamp(dt, 0.0, 0.05);

            scratchCartesian3 = Cesium.Cartesian3.normalize(
              particle.position,
              new Cesium.Cartesian3(),
            );
            scratchCartesian3 = Cesium.Cartesian3.multiplyByScalar(
              scratchCartesian3,
              -40.0 * dt,
              scratchCartesian3,
            );

            scratchCartesian3 = Cesium.Cartesian3.add(
              particle.position,
              scratchCartesian3,
              scratchCartesian3,
            );

            scratchCartographic = Cesium.Cartographic.fromCartesian(
              scratchCartesian3,
              Cesium.Ellipsoid.WGS84,
              scratchCartographic,
            );

            const angle = (Cesium.Math.PI * 2.0 * iteration) / options.numberOfSystems;
            iteration += options.iterationOffset;
            scratchCartographic.longitude +=
              Math.cos(angle) * options.cartographicStep * 30.0 * dt;
            scratchCartographic.latitude +=
              Math.sin(angle) * options.cartographicStep * 30.0 * dt;

            particle.position = Cesium.Cartographic.toCartesian(scratchCartographic);
          };
        };

        const matrix4Scratch = new Cesium.Matrix4();
        let scratchAngleForOffset = 0.0;
        const scratchOffset = new Cesium.Cartesian3();
        const imageSize = new Cesium.Cartesian2(15.0, 15.0);
        function createParticleSystems(options, systemsArray) {
          const length = options.numberOfSystems;
          for (let i = 0; i < length; ++i) {
            scratchAngleForOffset = (Math.PI * 2.0 * i) / options.numberOfSystems;
            scratchOffset.x += options.baseRadius * Math.cos(scratchAngleForOffset);
            scratchOffset.y += options.baseRadius * Math.sin(scratchAngleForOffset);

            const emitterModelMatrix = Cesium.Matrix4.fromTranslation(
              scratchOffset,
              matrix4Scratch,
            );
            const color = Cesium.Color.fromRandom(
              options.colorOptions[i % options.colorOptions.length],
            );
            const force = forceFunction(options, i);

            const item = viewer.scene.primitives.add(
              new Cesium.ParticleSystem({
                image: getImage(),
                startColor: color,
                endColor: color.withAlpha(0.0),
                particleLife: 3.5,
                speed: 0.00005,
                imageSize: imageSize,
                emissionRate: 30.0,
                emitter: new Cesium.CircleEmitter(0.1),
                lifetime: 0.1,
                updateCallback: force,
                modelMatrix: particlesModelMatrix,
                emitterModelMatrix: emitterModelMatrix,
              }),
            );
            systemsArray.push(item);
          }
        }

        const rocketSystems = [];
        const cometSystems = [];
        createParticleSystems(rocketOptions, rocketSystems);
        createParticleSystems(cometOptions, cometSystems);

        // toolbar elements
        function showAll(systemsArray, show) {
          const length = systemsArray.length;
          for (let i = 0; i < length; ++i) {
            systemsArray[i].show = show;
          }
        }

        const options = [
          {
            text: "Comet Tail",
            onselect: function () {
              showAll(rocketSystems, false);
              showAll(cometSystems, true);
              resetCamera();
            },
          },
          {
            text: "Rocket Thruster",
            onselect: function () {
              showAll(cometSystems, false);
              showAll(rocketSystems, true);
              resetCamera();
            },
          },
        ];
        Sandcastle.addToolbarMenu(options);
        showAll(cometSystems, true);
        //Sandcastle_End
      };

      if (typeof Cesium !== "undefined") {
        window.startupCalled = true;
        window.startup(Cesium).catch((error) => {
          "use strict";
          console.error(error);
        });
        Sandcastle.finishedLoading();
      }
    </script>
  </body>
</html>
